import logging
import multiprocessing
import os
from logging import getLevelName, config, handlers
"""
logging 采用了模块化设计，主要由四个部分组成:
(1) Loggers: 日志记录器，提供程序直接调用的接口
(2) Handers: 日志处理器，将记录的日志发送到指定的位置(终端打印or 保存到文件)
(3) Filters: 日志过滤器，提供更细粒度控制，决定哪些日志被输出
(4) Formatters: 日志格式器，用于控制信息的输出格式

Logging 中有 NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL这几种级别
"""

def worker(log_queue, log_name):
    """ 日志线程函数，从队列中取出日志并输出 """

    while True:
        record = log_queue.get()
        if record is None:
            break

        lg = logging.getLogger(log_name)
        lg.handle(record)


FORMAT = "%(asctime)s [%(process)d:%(threadName)s] [%(levelname)s] %(filename)s:%(lineno)s ==> %(message)s"


class LogManager(object):
    # 日志达到该大小后会新起一个log文件
    _LOG_MAX_BYTES = 100 * 1024 * 1024
    # 循环新建文件个数，超过该数字会覆盖旧日志文件
    _LOG_MAX_BACKUP_CNT = 10
    # 单例的实现
    _instance = None
    _init_end = False

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = object.__new__(cls)
        return cls._instance

    def __init__(self, log_name="sys", log_file="logs", log_level="INFO", *args, **kwargs):
        if not LogManager._init_end:
            LogManager._init_end = True
            self.log_processes = None
            self.log_name = log_name
            self._log_queue = None
            self.configure_logging_info(log_dir=log_file, level=log_level, log_name=log_name)
            self._logger = logging.getLogger(log_name)

    def get_logger(self):
        return self._logger

    def configure_logging_info(self, log_dir="logs", level='DEBUG', log_name="sys"):
        """
        配置日志记录信息
        :param log_dir:
        :param level:
        :param log_name:
        :return:
        """
        if not os.path.isdir(log_dir):
            os.makedirs(log_dir)

        log_file = f"{log_dir}/{log_name}.log"
        error_log_name = f"{log_dir}/error.log"
        configs = {
            'version': 1,
            #
            'formatters': {
                'detailed': {
                    'class': 'logging.Formatter',
                    'format': FORMAT
                    #     %(name)s            日志记录器的名称(日志通道)(logging channel)
                    #     %(levelno)s         日志记录消息的数字级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)
                    #     %(levelname)s       日志记录消息的文本级别 ("DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL")
                    #     %(pathname)s        发出日志记录调用的源文件的完整路径名(if available)
                    #     %(filename)s        路径名的文件名部分
                    #     %(module)s          模块(文件名的名称部分)(name portion of filename)
                    #     %(lineno)d          发出日志记录调用的源行号(if available)
                    #     %(funcName)s        函数名
                    #     %(created)f         LogRecord创建的时间(time.time() return value)
                    #     %(asctime)s         创建LogRecord的文本时间
                    #     %(msecs)d           创建时间中的毫秒数
                    #     %(relativeCreated)d 创建LogRecord时的时间(以毫秒为单位)，相对于日志模块加载的时间(通常在应用程序启动时)
                    #                         (typically at application startup time)
                    #     %(thread)d          线程ID (if available)
                    #     %(threadName)s      线程的名字 (if available)
                    #     %(process)d         进程ID (if available)
                    #     %(message)s         record. getmessage()的结果，在记录发出时计算得到
                }
            },
            # 处理器
            'handlers': {
                # 控制台输出
                'console': {
                    'class': 'logging.StreamHandler',
                    'formatter': 'detailed',
                    'level': getLevelName(level)
                },
                # info文件输出
                'info_file': {
                    'class': 'logging.handlers.RotatingFileHandler',
                    'filename': log_file,
                    'mode': 'a',
                    'formatter': 'detailed',
                    'maxBytes': self._LOG_MAX_BYTES,
                    'backupCount': self._LOG_MAX_BACKUP_CNT,
                    'level': getLevelName(level)
                },
                # error文件输出
                'errors': {
                    'class': 'logging.handlers.RotatingFileHandler',
                    'filename': error_log_name,
                    'mode': 'a',
                    'level': 'WARNING',
                    'formatter': 'detailed',
                    'maxBytes': self._LOG_MAX_BYTES,
                    'backupCount': self._LOG_MAX_BACKUP_CNT
                },
            },
            # 记录器
            'loggers': {
                log_name: {
                    'level': 'INFO',
                    'propagate': False,  # 避免日志调用root handler
                    'handlers': ['info_file', 'console', "errors"]
                }
            },
        }
        config.dictConfig(configs)

    def start_multiprocess_logging(self):
        if self.log_processes is None:
            self.multiprocess_logging()

    def multiprocess_logging(self):
        """
            多进程日志记录
            :return:
        """
        self._log_queue = multiprocessing.Manager().Queue(-1)
        # 进程日志配置，将日志输入队列handler
        queue_handler = handlers.QueueHandler(self._log_queue)
        queue_handler.setLevel(20)
        process_log = logging.getLogger("process")
        if process_log.hasHandlers():
            process_log.handlers.clear()
        process_log.setLevel(20)
        process_log.addHandler(queue_handler)
        self.log_processes = multiprocessing.Process(target=worker, args=(self._log_queue, self.log_name), daemon=True)
        self.log_processes.start()
